package com.dmgis.hndz.common.utils;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.Cipher;
import javax.crypto.KeyAgreement;
import javax.crypto.SecretKey;
import javax.crypto.interfaces.DHPrivateKey;
import javax.crypto.interfaces.DHPublicKey;
import javax.crypto.spec.DHParameterSpec;
import java.io.UnsupportedEncodingException;
import java.security.*;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

/**
 * @author jianggujin
 * @ClassName CryptoUtil
 * @Description TODO
 * Diffie-Hellman算法(D-H算法)，密钥一致协议。是由公开密钥密码体制的奠基人Diffie和Hellman所提出的一种思想。
 * 简单的说就是允许两名用户在公开媒体上交换信息以生成"一致"的、可以共享的密钥。换句话说，就是由甲方产出一对密钥（公钥、私钥），
 * 乙方依照甲方公钥产生乙方密钥对（公钥、私钥）。以此为基线，作为数据传输保密基础，同时双方使用同一种对称加密算法构建本地密钥（SecretKey）对数据加密
 * 。这样，在互通了本地密钥（SecretKey）算法后，甲乙双方公开自己的公钥，使用对方的公钥和刚才产生的私钥加密数据，
 * 同时可以使用对方的公钥和自己的私钥对数据解密。不单单是甲乙双方两方，可以扩展为多方共享数据通讯，这样就完成了网络交互数据的安全通讯！
 * 该算法源于中国的同余定理——中国馀数定理
 * 甲方构建密钥对儿，将公钥公布给乙方，将私钥保留；双方约定数据加密算法；乙方通过甲方公钥构建密钥对儿，将公钥公布给甲方，将私钥保留。
 * 甲方使用私钥、乙方公钥、约定数据加密算法构建本地密钥，然后通过本地密钥加密数据，发送给乙方加密后的数据；乙方使用私钥、甲方公钥、
 * 约定数据加密算法构建本地密钥，然后通过本地密钥对数据解密。
 * 乙方使用私钥、甲方公钥、约定数据加密算法构建本地密钥，然后通过本地密钥加密数据，发送给甲方加密后的数据；甲方使用私钥、乙方公钥、
 * 约定数据加密算法构建本地密钥，然后通过本地密钥对数据解密。
 * @Author sun
 * @Date 2021/1/20 16:11
 * @Version 1.0
 **/
public class CryptoUtil {

    public static final String ALGORITHM = "DH";

    /**
     * 默认密钥字节数
     *
     * <pre>
     * DH
     * Default Keysize 1024
     * Keysize must be a multiple of 64, ranging from 512 to 1024 (inclusive).
     * </pre>
     */
    private static final int KEY_SIZE = 1024;

    /**
     * DH加密下需要一种对称加密算法对数据加密，这里我们使用DES，也可以使用其他对称加密算法。
     */
    public static final String SECRET_ALGORITHM = "DESede";//DES
    private static final String PUBLIC_KEY = "SZDMGIS";
    private static final String PRIVATE_KEY = "SZDMGISDCPJ";

    /**
     * 初始化甲方密钥
     *
     * @return
     * @throws Exception
     */
    public static Map<String, Object> initKey() throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator
                .getInstance(ALGORITHM);
        keyPairGenerator.initialize(KEY_SIZE);

        KeyPair keyPair = keyPairGenerator.generateKeyPair();

        // 甲方公钥
        DHPublicKey publicKey = (DHPublicKey) keyPair.getPublic();

        // 甲方私钥
        DHPrivateKey privateKey = (DHPrivateKey) keyPair.getPrivate();

        Map<String, Object> keyMap = new HashMap<String, Object>(2);

        keyMap.put(PUBLIC_KEY, publicKey);
        keyMap.put(PRIVATE_KEY, privateKey);
        return keyMap;
    }

    //转特殊字符
    public static String getParam(String Param) {
        String param=Param;
        for (int i = 0; i < Param.length(); i++) {
            char ch = Param.charAt(i);
            if("/".equals(ch+"")){
                param=param.replace("/","%2F");
            }else if("+".equals(ch+"")){
                param=param.replace("+","%2B");
            }
        }
        return param;
    }

    /**
     * 初始化乙方密钥
     *
     * @param key 甲方公钥
     * @return
     * @throws Exception
     */
    public static Map<String, Object> initKey(String key) throws Exception {
        // 解析甲方公钥
        byte[] keyBytes = decryptBASE64(key);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(keyBytes);
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);

        // 由甲方公钥构建乙方密钥
        DHParameterSpec dhParamSpec = ((DHPublicKey) pubKey).getParams();

        KeyPairGenerator keyPairGenerator = KeyPairGenerator
                .getInstance(keyFactory.getAlgorithm());
        keyPairGenerator.initialize(dhParamSpec);

        KeyPair keyPair = keyPairGenerator.generateKeyPair();

        // 乙方公钥
        DHPublicKey publicKey = (DHPublicKey) keyPair.getPublic();

        // 乙方私钥
        DHPrivateKey privateKey = (DHPrivateKey) keyPair.getPrivate();

        Map<String, Object> keyMap = new HashMap<String, Object>(2);

        keyMap.put(PUBLIC_KEY, publicKey);
        keyMap.put(PRIVATE_KEY, privateKey);

        return keyMap;
    }

    /**
     * 加密<br>
     *
     * @param data       待加密数据
     * @param publicKey  甲方公钥
     * @param privateKey 乙方私钥
     * @return
     * @throws Exception
     */
    public static byte[] encrypt(byte[] data, String publicKey,
                                 String privateKey) throws Exception {
        // 生成本地密钥
        SecretKey secretKey = getSecretKey(publicKey, privateKey);
        // 数据加密
        Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
        cipher.init(Cipher.ENCRYPT_MODE, secretKey);
        return cipher.doFinal(data);
    }

    /**
     * 解密<br>
     *
     * @param data       待解密数据
     * @param publicKey  乙方公钥
     * @param privateKey 乙方私钥
     * @return
     * @throws Exception
     */
    public static byte[] decrypt(byte[] data, String publicKey,
                                 String privateKey) throws Exception {
        // 生成本地密钥
        SecretKey secretKey = getSecretKey(publicKey, privateKey);
        // 数据解密
        Cipher cipher = Cipher.getInstance(secretKey.getAlgorithm());
        cipher.init(Cipher.DECRYPT_MODE, secretKey);
        return cipher.doFinal(data);
    }

    /**
     * 构建密钥
     *
     * @param publicKey  公钥
     * @param privateKey 私钥
     * @return
     * @throws Exception
     */
    private static SecretKey getSecretKey(String publicKey, String privateKey)
            throws Exception {
        // 初始化公钥
        byte[] pubKeyBytes = decryptBASE64(publicKey);
        KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM);
        X509EncodedKeySpec x509KeySpec = new X509EncodedKeySpec(pubKeyBytes);
        PublicKey pubKey = keyFactory.generatePublic(x509KeySpec);
        // 初始化私钥
        byte[] priKeyBytes = decryptBASE64(privateKey);
        PKCS8EncodedKeySpec pkcs8KeySpec = new PKCS8EncodedKeySpec(priKeyBytes);
        Key priKey = keyFactory.generatePrivate(pkcs8KeySpec);
        KeyAgreement keyAgree = KeyAgreement.getInstance(keyFactory
                .getAlgorithm());
        keyAgree.init(priKey);
        keyAgree.doPhase(pubKey, true);
        // 生成本地密钥
        SecretKey secretKey = keyAgree.generateSecret(SECRET_ALGORITHM);
        return secretKey;
    }

    /**
     * 取得私钥
     *
     * @param keyMap
     * @return
     * @throws Exception
     */
    public static String getPrivateKey(Map<String, Object> keyMap)
            throws Exception {
        Key key = (Key) keyMap.get(PRIVATE_KEY);
        return encryptBASE64(key.getEncoded());
    }

    /**
     * 取得公钥
     *
     * @param keyMap
     * @return
     * @throws Exception
     */
    public static String getPublicKey(Map<String, Object> keyMap)
            throws Exception {
        Key key = (Key) keyMap.get(PUBLIC_KEY);
        return encryptBASE64(key.getEncoded());
    }

    /**
     * BASE64解密
     *
     * @param key
     * @return
     * @throws Exception
     */
    public static byte[] decryptBASE64(String key) throws Exception {
        return (new BASE64Decoder()).decodeBuffer(key);
    }

    /**
     * BASE64加密
     *
     * @param key
     * @return
     * @throws Exception
     */
    public static String encryptBASE64(byte[] key) throws Exception {
        return (new BASE64Encoder()).encodeBuffer(key);
    }

    /****
     * Base64的encode和decode的通用方法
     * @param str
     * @return
     */
//    public static String  encodeBase64Str(String str) {
//        try {
//            String result = new String(Base64.getEncoder().encode(str.getBytes()), "UTF-8");
//            return result;
//        } catch (UnsupportedEncodingException e) {
//            e.printStackTrace();
//        }
//        return null;
//    }
    public static String encodeBase64Str(String str) {
        try {
            //String result = new String(Base64.getEncoder().encode(str.getBytes()), "UTF-8");
            String result = Base64.getEncoder().encodeToString(str.getBytes("UTF-8"));
            return result;
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }

    /****
     * Base64的encode和decode的通用方法
     * @param str
     * @return
     */
    public static String decodeBase64(String str) {
        try {
            byte[] result = Base64.getDecoder().decode(str.getBytes("UTF-8"));
            return new String(result);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static void main(String[] args) throws Exception {
        String aInput = "1=1 and ybba01a030>='2018-01-01 00:00:00' and ybba01a030<='2018-12-31 23:59:59'";
        System.out.println("http://localhost:8078/apiservice/YB/SelectYbba01A?param=" + encodeBase64Str(aInput));
        // 生成甲方密钥对儿
//        Map<String, Object> aKeyMap = initKey();
//        String aPublicKey = getPublicKey(aKeyMap);
//        String aPrivateKey = getPrivateKey(aKeyMap);
//
//        System.err.println("甲方公钥:\r" + aPublicKey);
//        System.err.println("甲方私钥:\r" + aPrivateKey);
//
//        // 由甲方公钥产生本地密钥对儿
//        Map<String, Object> bKeyMap = initKey(aPublicKey);
//        String bPublicKey = getPublicKey(bKeyMap);
//        String bPrivateKey = getPrivateKey(bKeyMap);
//
//        System.err.println("乙方公钥:\r" + bPublicKey);
//        System.err.println("乙方私钥:\r" + bPrivateKey);
//
//        String aInput = "http://localhost:8078/apiservice/YB/SelectYbba01A?param=1=1 and ybba01a030>='2018-01-01 00:00:00' and ybba01a030<='2018-12-31 23:59:59'";
//        System.err.println("原文: " + aInput);
//
//        // 由甲方公钥，乙方私钥构建密文
//        byte[] aCode = encrypt(aInput.getBytes(), aPublicKey,
//                bPrivateKey);
//        System.err.println("编码: " + new String(aCode));
//        // 由乙方公钥，甲方私钥解密
//        byte[] aDecode = decrypt(aCode, bPublicKey, aPrivateKey);
//        String aOutput = (new String(aDecode));
//
//        System.err.println("解密: " + aOutput);
//
//        System.err.println(" ===============反过来加密解密================== ");
//        String bInput = "http://dzzh.hndzhj.com/dmgisapiservice/Zhaa03B/SelectZhaa03B?param= ZHAA03B090 != 'N' or ZHAA03B090 IS NULL order by zhaa03b010";
//        System.err.println("原文: " + bInput);
//
//        // 由乙方公钥，甲方私钥构建密文
//        byte[] bCode = encrypt(bInput.getBytes(), bPublicKey,
//                aPrivateKey);
//        System.err.println("编码: " + new String(bCode));
//        // 由甲方公钥，乙方私钥解密
//        byte[] bDecode = decrypt(bCode, aPublicKey, bPrivateKey);
//        String bOutput = (new String(bDecode));
//
//        System.err.println("解密: " + bOutput);

    }

}

